/*
 * Position.java.
 */
package com.phonemap.models;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

import org.json.JSONException;
import org.json.JSONObject;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.phonemap.common.AppConstant;
import com.phonemap.common.Util;
import com.phonemap.dto.PositionDTO;
import com.phonemap.helper.PositionHelper;
import com.phonemap.search.SearchIndexManager;

/**
 * The Class Position.
 */
public final class Position {

	/** The Constant NAME_FIELD. */
	public static final String POSITION_ID_FIELD = "position_id";
	
	/** The user id field. */
	public static final String USER_ID_FIELD = "user_id";

	/** The latitude field. */
	public static final String LATITUDE_FIELD = "latitude";

	/** The longitude field. */
	public static final String LONGITUDE_FIELD = "longitude";

	/** The Constant LOCATED_FIELD. */
	public static final String LOCATED_FIELD = "located";

	/** The located time field. */
	public static final String LOCATED_TIME_FIELD = "located_time";

	/**
	 * Instantiates a new position.
	 */
	private Position() {

	}

	/**
	 * Insert position.
	 * 
	 * @param userId
	 *            the user id
	 * @param located
	 *            the located
	 * @param locatedTime
	 *            the located time
	 * @return the entity
	 */
	public static Entity insertPosition(final long userId, final GeoPt located,
			final Date locatedTime) {
		Entity user = User.getUser(userId);
		if (user != null) {
			return insertPosition(user, located, locatedTime);
		}
		return null;
	}

	/**
	 * Insert position.
	 * 
	 * @param parent
	 *            the parent
	 * @param located
	 *            the located
	 * @param locatedTime
	 *            the located time
	 * @return the entity
	 */
	public static Entity insertPosition(final Entity parent,
			final GeoPt located, final Date locatedTime) {
		Entity position = new Entity(AppConstant.ENTITY_POSITION,
				parent.getKey());
		position.setProperty(USER_ID_FIELD, parent.getKey().getId());
		position.setProperty(LOCATED_FIELD, located);
		position.setProperty(LOCATED_TIME_FIELD, locatedTime);

		// insert into GAE
		Util.persistEntity(position);

		// input index
		SearchIndexManager.INSTANCE.indexDocument(PositionHelper.getIndex(),
				PositionHelper.buildDocument(position.getKey().getId(), parent
						.getKey().getId(), located, locatedTime));
		return position;
	}

	// /**
	// * Gets the single position.
	// *
	// * @return the single position
	// */
	// public static Entity getSinglePosition(final long user) {
	// Iterable<Entity> results = Util.listEntities(
	// AppConstant.ENTITY_POSITION, AppConstant.ENTITY_USER, user);
	// List<Entity> entity = new ArrayList<Entity>();
	// for (Entity e : results) {
	// if (e != null) {
	// entity.add(e);
	// }
	// }
	// if (!entity.isEmpty()) {
	// return (Entity) entity.remove(0);
	// }
	// return null;
	// }

	// public static Entity getSinglePosition(final long user) {
	// Iterable<Entity> results = Util.listEntities(
	// AppConstant.ENTITY_POSITION, AppConstant.ENTITY_USER, user);
	// List<Entity> entity = new ArrayList<Entity>();
	// for (Entity e : results) {
	// if (e != null) {
	// entity.add(e);
	// }
	// }
	// if (!entity.isEmpty()) {
	// return (Entity) entity.remove(0);
	// }
	// return null;
	// }

	/**
	 * Gets the all positions.
	 * 
	 * @return the all positions
	 */
	public static Iterable<Entity> getAllPositions() {
		Iterable<Entity> entities = Util.listEntities(
				AppConstant.ENTITY_POSITION, null, null);
		return entities;
	}

	/**
	 * Gets the all positions.
	 * 
	 * @param kind
	 *            the kind
	 * @return the all positions
	 */
	public static Iterable<Entity> getAllPositions(final String kind) {
		Iterable<Entity> entities = Util.listEntities(kind, null, null);
		return entities;
	}

	/**
	 * Gets the positions.
	 * 
	 * @param userId
	 *            the user id
	 * @return the positions
	 */
	public static Iterable<Entity> getPositions(final long userId) {
		Key ancestorKey = KeyFactory.createKey(AppConstant.ENTITY_USER, userId);
		return Util.listChildren(AppConstant.ENTITY_POSITION, ancestorKey);
	}

	/**
	 * Gets the positions.
	 * 
	 * @param userId
	 *            the user id
	 * @param sortDirection
	 *            the sort direction
	 * @return the positions
	 */
	public static Iterable<Entity> getPositions(final long userId,
			final SortDirection sortDirection) {
		Key ancestorKey = KeyFactory.createKey(AppConstant.ENTITY_USER, userId);
		return Util.listChildren(AppConstant.ENTITY_POSITION, ancestorKey,
				sortDirection);
	}

	/**
	 * Gets the position.
	 * 
	 * @param id
	 *            the id
	 * @param userId
	 *            the user id
	 * @return the position
	 */
	public static Entity getPosition(final long id, final long userId) {
		Key parentKey = KeyFactory.createKey(AppConstant.ENTITY_USER, userId);
		Key key = KeyFactory.createKey(parentKey, AppConstant.ENTITY_POSITION,
				id);
		Entity entity = Util.findEntity(key);
		return entity;
	}

	/**
	 * Gets the position by id.
	 * 
	 * @param positionId
	 *            the position id
	 * @return the position by id
	 */
	public static Entity getPositionById(final long positionId) {
		Key key = KeyFactory.createKey(AppConstant.ENTITY_POSITION, positionId);
		return Util.findEntity(key);
	}

	/**
	 * Gets the related phone posstions.
	 * 
	 * @param phone
	 *            the phone
	 * @return the related phone posstions
	 */
	public static Iterable<Entity> getRelatedPhonePosstions(final String phone) {
		Iterable<Entity> userList = User.getAllUsers();
		List<Entity> positionList = new ArrayList<Entity>();
		for (Entity user : userList) {
			Entity e = Util.getMaxEntities(AppConstant.ENTITY_POSITION,
					USER_ID_FIELD, user.getKey().getName(), LOCATED_TIME_FIELD);
			if (e != null) {
				if (phone.equals(e.getProperty(USER_ID_FIELD).toString())) {
					if (positionList.size() > 0) {
						positionList.add(positionList.get(0));
						positionList.set(0, e);
					} else {
						positionList.add(0, e);
					}
				} else {
					positionList.add(e);
				}

			}
		}
		return positionList;
	}

	/**
	 * Delete position.
	 * 
	 * @param id
	 *            the id
	 * @param userId
	 *            the user id
	 * @return the string
	 */
	public static String deletePosition(final long id, final long userId) {
		Entity entity = getPosition(id, userId);
		if (entity != null) {
			Util.deleteEntity(entity.getKey());
			return AppConstant.SUCCESS_STATUS;
		} else {
			return AppConstant.POSITION_NOT_FOUND;
		}
	}

	/**
	 * Delete position.
	 * 
	 * @param positionId
	 *            the position id
	 * @return the string
	 */
	public static String deletePosition(final long positionId) {
		Entity entity = getPositionById(positionId);
		if (entity != null) {
			Util.deleteEntity(entity.getKey());
			return AppConstant.SUCCESS_STATUS;
		} else {
			return AppConstant.POSITION_NOT_FOUND;
		}
	}

	/**
	 * Delete positions.
	 * 
	 * @param kind
	 *            the kind
	 * @param userName
	 *            the user name
	 * @return the string
	 */
	// public static String deletePositions(final String parentKey) {
	// Iterable<Entity> entities = getPositions(parentKey);
	// if (entities != null) {
	// for (Entity position : entities) {
	// Util.deleteEntity(position.getKey());
	// }
	// return AppConstant.SUCCESS_STATUS;
	// } else {
	// return AppConstant.POSITION_NOT_FOUND;
	// }
	// }

	/**
	 * Get all the positions for a user.
	 * 
	 * @param kind
	 *            : position kind
	 * @param userName
	 *            : user name
	 * @return the positions for user
	 * @return: all positions of type user
	 */
	public static Iterable<Entity> getPositionsForUser(final String kind,
			final long userName) {
		Key ancestorKey = KeyFactory.createKey(AppConstant.ENTITY_USER,
				userName);
		return Util.listChildren(AppConstant.ENTITY_POSITION, ancestorKey);
	}

	/**
	 * Gets the history.
	 * 
	 * @param userId
	 *            the user id
	 * @param days
	 *            the days
	 * @param offset
	 *            the offset
	 * @return the history
	 */
	public static List<PositionDTO> getHistory(final long userId,
			final int days, final int offset) {

		return PositionHelper.getHistory(userId, days, offset);
	}
	
	/**
	 * Builds the index.
	 *
	 * @param positionId the position id
	 * @param userId the user id
	 * @param located the located
	 * @param locatedTime the located time
	 */
	public static void buildIndex(final long positionId, final long userId,
			final GeoPt located, final Date locatedTime) {
		PositionHelper.deleteOldIndex(positionId);
		SearchIndexManager.INSTANCE.indexDocument(PositionHelper.getIndex(),
				PositionHelper.buildDocument(positionId, userId, located,
						locatedTime));

	}
	
	/**
	 * Insert position.
	 *
	 * @param json the json
	 * @throws JSONException the JSON exception
	 */
	public static void insertPosition(final JSONObject json) throws JSONException {
		try {
			if (json != null) {
				DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				dateFormat.setTimeZone(TimeZone.getTimeZone(AppConstant.TIME_ZONE));
				long userId = json.getLong("user_id");
				float latitude = (float) json.getDouble("latitude");
				float longitude = (float) json.getDouble("longitude");
				Date locatedTime = dateFormat.parse(json.getString("located_time"));
				Position.insertPosition(userId, new GeoPt(latitude, longitude), locatedTime);
			}
		} catch (Exception e) {
			throw new JSONException(e.getMessage());
		}
		
	}
}
